use std::{
    collections::HashMap,
    convert::Infallible,
    hash::Hash,
    sync::{Arc, Mutex},
    time::{Duration, SystemTime},
};

use axum::{
    body::Body, extract::State, handler::Handler, http::Request, middleware::Next,
    response::Response, routing::MethodRouter, Json,
};

use crate::statics::SERVER_CONFIG;
use std::future::Future;

// #[derive(Debug, Clone)]
// pub struct CacheStore {
//     map: Arc<Mutex<HashMap<Request<Body>, (SystemTime, Response<Body>)>>>,
// }

// impl CacheStore
// {
//     pub fn new() -> Self {
//         Self {
//             map: Arc::new(Mutex::new(HashMap::<Request<Body>, (SystemTime, Response<Body>)>::new())),
//         }
//     }
//     pub async fn add(&self, key: Request<Body>, val: Response<Body>) {
//         let mut map = self.map.try_lock().unwrap();
//         let time = SystemTime::now();
//         let _ = map.insert(key, (time, val));
//     }
//     pub async fn get(&self, key: &Request<Body>) -> Option<Response<Body>> {
//         if let Ok(o) = self.map.try_lock() {
//             if let Some((t, v)) = o.get(key) {
//                 let time = SystemTime::now();
//                 if let Ok(dur) = time.duration_since(*t) {
//                     if dur < SERVER_CONFIG.cache_dur {
//                         return Some(v.clone());
//                     }
//                 }
//             }
//         }
//         None
//     }
// }

// pub async fn cache_layer<B>(
//     State(cache): State<CacheStore>,
//     query : Request<B>,
//     next: Next,
// ) -> Response<Body>
// where B : Clone
// {
//     if let Some(s) = cache.get(&query).await {
//         s
//     } else {
//         let a = next.run(query.clone()).await;
//         cache.add(query,a.clone());
//         a
//     }
// }

#[derive(Debug, Clone)]
pub struct CacheStore<Q, A>
where
    Q: Eq + Hash + Clone,
    A: Clone,
{
    map: Arc<Mutex<HashMap<Q, (SystemTime, A)>>>,
    cache_dur: Duration,
}

impl<Q, A> CacheStore<Q, A>
where
    Q: Eq + Clone + Hash,
    A: Clone,
{
    pub fn new(cache_dur: Duration) -> Self {
        Self {
            map: Arc::new(Mutex::new(HashMap::<Q, (SystemTime, A)>::new())),
            cache_dur,
        }
    }
    pub async fn add(&self, key: Q, val: A) {
        let mut map = self.map.try_lock().unwrap();
        let time = SystemTime::now();
        let _ = map.insert(key, (time, val));
    }
    pub async fn get(&self, key: &Q) -> Option<A> {
        if let Ok(o) = self.map.try_lock() {
            if let Some((t, v)) = o.get(key) {
                let time = SystemTime::now();
                if let Ok(dur) = time.duration_since(*t) {
                    if dur < SERVER_CONFIG.cache_dur {
                        return Some(v.clone());
                    }
                }
            }
        }
        None
    }
    pub async fn apply<F>(&self, query: Q, orin: fn(Q) -> F) -> A
    where
        F: Future<Output = A>,
    {
        if let Some(some) = self.get(&query).await {
            some
        } else {
            let answer = orin(query.clone()).await;
            let _ = self.add(query, answer.clone());
            answer
        }
    }
}
